<template>
  <div>
    <h3>会议室 {{ roomId }}</h3>
    <video ref="localVideo" autoplay muted></video>
    <div v-for="peer in peers" :key="peer.uid">
      <video :ref="'video_'+peer.uid" autoplay></video>
      <span>{{ peer.nick }}</span>
    </div>
  </div>
</template>

<script>
import { connect } from 'nats.ws'
import adapter from 'webrtc-adapter'

export default {
  data() {
    return {
      roomId: 'demo',
      localStream: null,
      peers: {},            // {uid: {pc, nick}}
      nc: null
    }
  },
  async mounted() {
    this.nc = await connect({ servers: 'ws://localhost:9222' }) // NATS 端口
    await this.joinRoom()
    this.subscribe()
  },
  methods: {
    async joinRoom() {
      const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true })
      this.localStream = stream
      this.$refs.localVideo.srcObject = stream
      this.nc.publish(`room.${this.roomId}.join`, { uid: this.uid, nick: '我' })
    },
    subscribe() {
      // 1. 新人加入 —— 创建 RTCPeerConnection
      this.nc.subscribe(`room.${this.roomId}.join`, (_, msg) => {
        const { uid, nick } = JSON.parse(msg.data)
        if (uid === this.uid) return
        this.createPeer(uid, nick, true) // 主动发起
      })

      // 2. 收到 offer
      this.nc.subscribe(`room.${this.roomId}.sdp.offer`, (_, msg) => {
        const { fromUid, toUid, sdp } = JSON.parse(msg.data)
        if (toUid !== this.uid) return
        this.createPeer(fromUid, '', false) // 被动接收
        this.peers[fromUid].pc.setRemoteDescription(new RTCSessionDescription(sdp))
        this.peers[fromUid].pc.createAnswer()
            .then(answer => {
              this.peers[fromUid].pc.setLocalDescription(answer)
              this.nc.publish(`room.${this.roomId}.sdp.answer`, { fromUid: this.uid, toUid: fromUid, sdp: answer })
            })
      })

      // 3. 收到 answer
      this.nc.subscribe(`room.${this.roomId}.sdp.answer`, (_, msg) => {
        const { fromUid, toUid, sdp } = JSON.parse(msg.data)
        if (toUid !== this.uid) return
        this.peers[fromUid].pc.setRemoteDescription(new RTCSessionDescription(sdp))
      })

      // 4. 收到 ICE
      this.nc.subscribe(`room.${this.roomId}.ice`, (_, msg) => {
        const { fromUid, toUid, candidate } = JSON.parse(msg.data)
        if (toUid !== this.uid) return
        this.peers[fromUid].pc.addIceCandidate(new RTCIceCandidate(candidate))
      })
    },
    createPeer(uid, nick, isOffer) {
      const pc = new RTCPeerConnection({
        iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
      })
      this.$set(this.peers, uid, { pc, nick })
      this.localStream.getTracks().forEach(t => pc.addTrack(t, this.localStream))

      pc.onicecandidate = e => {
        if (e.candidate) {
          this.nc.publish(`room.${this.roomId}.ice`, { fromUid: this.uid, toUid: uid, candidate: e.candidate })
        }
      }
      pc.ontrack = e => {
        this.$nextTick(() => {
          this.$refs[`video_${uid}`][0].srcObject = e.streams[0]
        })
      }
      if (isOffer) {
        pc.createOffer().then(offer => {
          pc.setLocalDescription(offer)
          this.nc.publish(`room.${this.roomId}.sdp.offer`, { fromUid: this.uid, toUid: uid, sdp: offer })
        })
      }
    }
  },
  beforeDestroy() {
    this.localStream?.getTracks().forEach(t => t.stop())
    Object.values(this.peers).forEach(p => p.pc.close())
    this.nc?.close()
  }
}
</script>